<template>
  <div class="transfer">
    <!-- 左侧列表 -->
    <div class="transfer__list left" :style="listStyle">
      <div class="list__header" v-if="!hideTitle">{{ leftTitle }}</div>
      <div class="btn-box scroll-box">
        <q-btn
          v-for="item in leftData"
          :key="item[valueKey]"
          class="btn"
          @click="_moveToRight(item[valueKey])"
          unelevated
        >
          <template>
            <div class="btn-wrapper">
              <span style="width:140px" class="label text-ellipsis">
                {{ item[labelKey] }}
                <q-tooltip>{{ item[labelKey] }}</q-tooltip>
              </span>
              <q-icon class="arrow-icon" name="iconfont icontubiao-43" />
            </div>
          </template>
        </q-btn>
      </div>
    </div>
    <!-- 右侧列表 -->
    <div class="transfer__list right" :style="listStyle">
      <div class="list__header" v-if="!hideTitle">{{ rightTitle }}</div>
      <div class="btn-box scroll-box">
        <q-btn
          v-for="item in rightData"
          :key="item[valueKey]"
          class="btn"
          @click="_moveToLeft(item[valueKey])"
          unelevated
        >
          <template>
            <div class="btn-wrapper">
              <q-icon class="arrow-icon" name="iconfont icontubiao-44" />
              <span style="width:140px" class="label text-ellipsis">
                {{ item[labelKey] }}
                <q-tooltip>{{ item[labelKey] }}</q-tooltip>
              </span>
            </div>
          </template>
        </q-btn>
      </div>
    </div>
  </div>
</template>

<script>
import _ from 'lodash'
export default {
  name: 'index',
  props: {
    hideTitle: {
      type: Boolean,
      default: false
    },
    /**
     * 每个穿梭框的样式（Object 的形式表示）
     * @default { height: '480px', width: '200px' }
     */
    styles: {
      type: Object,
      default: () => {
        return {
          height: '480px',
          width: '200px'
        }
      }
    },

    /**
     * 穿梭框源数据，初始显示在左栏中
     * 默认情况下，源数据中 value 字段为数据的值，label 字段为显示的标签
     * 若要修改值或标签的键名，请指定 {@link #valueKey} 或 {@link #labelKey}
     * @default []
     */
    sourceData: {
      type: Array,
      default: () => []
    },

    /**
     * 被选择到右栏的数据的值
     * 值根据 {@link #valueKey} 从 {@link #data} 中取得
     * required
     */
    value: {
      type: Array,
      required: true
    },
    rightList: Array,
    /**
     * 在源数据中，表示数据值的键名
     * @see #data
     * @default 'value'
     */
    valueKey: {
      type: String,
      default: 'value'
    },

    /**
     * 在源数据中，表示标签的键名
     * @see #data
     * @default 'label
     */
    labelKey: {
      type: String,
      default: 'label'
    },

    /**
     * 左侧列表的标题
     * @default 'Left'
     */
    leftTitle: {
      type: String,
      default: 'Left'
    },

    /**
     * 右侧列表的标题
     * @default 'Right'
     */
    rightTitle: {
      type: String,
      default: 'Right'
    }
  },
  data () {
    return {
      // 左栏数据
      leftData: this.sourceData,

      // 右栏数据
      rightData: [],

      // 选中的值
      selectedValues: []
    }
  },
  methods: {
    /**
     * 移动到右栏
     * @param value 欲移动的数据的 value
     */
    _moveToRight: function (value) {
      this._moveToAnother(this.leftData, this.rightData, value)
      // 若已选择的 value 数组中无，则插入该数组
      !this.selectedValues.includes(value) && this.selectedValues.push(value)
    },

    /**
     * 移动到左栏
     * @param value 欲移动的数据的 value
     */
    _moveToLeft: function (value) {
      this._moveToAnother(this.rightData, this.leftData, value)
      // 从已选择的 value 数组中移除
      this.selectedValues = this.selectedValues.filter(val => val !== value)
    },

    /**
     * 移动到另一栏
     * @param from {Array} 源栏
     * @param to {Array} 目标栏
     * @param value 欲移动的数据的 value
     */
    _moveToAnother: function (from, to, value) {
      // 筛选出 value 所属数据的数组下标
      let indices = from.reduce((acc, datum, index) => {
        if (datum[this.valueKey] === value) {
          acc.push(index)
        }
        return acc
      }, [])

      // 从 from 中删除，push 到 to
      indices.forEach(index => {
        to.push(...from.splice(index, 1))
      })
    }
  },
  computed: {
    // 每个穿梭框的样式
    listStyle: function () {
      return Object.assign({}, this.styles)
    }
  },
  watch: {
    // 源数据
    sourceData: function (newVal) {
      // 两栏已有的数据的 value
      let existingValues = this.leftData.map(datum => datum[this.valueKey])
        .concat(this.rightData.map(datum => datum[this.valueKey]))

      // 过滤出新增的数据
      let diffSet = newVal.filter(datum => !existingValues.includes(datum[this.valueKey]))
      if (diffSet.length === 0) {
        return
      }
      // 追加新数据到左栏
      this.leftData.push(diffSet)
    },
    rightList: {
      handler (newValue) {
        if (!_.isEmpty(newValue)) {
          this.rightData = newValue
          this.leftData = _.differenceBy(this.sourceData, newValue, 'id')
          this.selectedValues = newValue.map(v => v.id)
        }
      },
      deep: true,
      immediate: true
    },
    // 选中的值
    selectedValues: {
      deep: true,
      immediate: true,
      handler () {
        this.$emit('input', this.selectedValues)
      }
    }
  }
}
</script>

<style scoped lang="stylus">
  .transfer {
    display flex

    &__list {
      display flex
      flex-direction column
      position relative
      background white
      border 1px solid #D3D3D3
      border-radius 6px
      overflow-x hidden
      overflow-y: auto;

      &.left {
        margin-right 20px
      }

      .list__header {
        background $primary
        color white
        height 32px
        width calc(100% + 2px)
        margin-left -1px
        line-height 32px
        padding 0 10px
        font-size 16px
        border-radius 6px 6px 0 0
      }

      .btn-box {
        display flex
        flex-direction column
        padding 10px
        overflow auto

        .btn {
          box-shadow 0 0 3px 1px #D3D3D3
          color #333
          padding 0
          height 32px
          width 100%

          &:not(:last-child) {
            margin-bottom 11px
          }

          .btn-wrapper {
            width 100%
            display flex
            justify-content space-between
            align-items center
            padding 0 10px

            .label {
              font-size 14px
            }

            .arrow-icon {
              font-size 18px
            }
          }
        }
      }

      &.right .btn-box .btn {
        background #CFD6FE
      }
    }
  }
</style>
